﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

namespace Tools.Collections
{
    //来自 http://www.codeproject.com/Articles/38908/Thread-Safe-Generic-Queue-Class.aspx

    /// <summary>
    /// Represents a First-In, First-Out thread-safe collection of objects
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ThreadSafeQueue<T> : ICollection
    {
        #region Variables

        /// <summary>
        /// The private q which holds the actual data
        /// </summary>
        private readonly Queue<T> _queue;

        /// <summary>
        /// Lock for the Q
        /// </summary>
        private readonly ReaderWriterLockSlim lockQ = new ReaderWriterLockSlim();

        /// <summary>
        /// Used only for the SyncRoot properties
        /// </summary>
        private readonly object objSyncRoot = new object();

        // Variables
        #endregion

        #region Init

        /// <summary>
        /// Initializes the Queue
        /// </summary>
        public ThreadSafeQueue()
        {
            _queue = new Queue<T>();
        }

        /// <summary>
        /// Initializes the Queue
        /// </summary>
        /// <param name="capacity">the initial number of elements the queue can contain</param>
        public ThreadSafeQueue(int capacity)
        {
            _queue = new Queue<T>(capacity);
        }

        /// <summary>
        /// Initializes the Queue
        /// </summary>
        /// <param name="collection">the collection whose members are copied to the Queue</param>
        public ThreadSafeQueue(IEnumerable<T> collection)
        {
            _queue = new Queue<T>(collection);
        }

        // Init
        #endregion

        #region IEnumerable<T> Members

        /// <summary>
        /// Returns an enumerator that enumerates through the collection
        /// </summary>
        public IEnumerator<T> GetEnumerator()
        {
            Queue<T> localQ;

            // init enumerator
            lockQ.EnterReadLock();
            try
            {
                // create a copy of m_TList
                localQ = new Queue<T>(_queue);
            }
            finally
            {
                lockQ.ExitReadLock();
            }

            // get the enumerator
            foreach (T item in localQ)
                yield return item;
        }

        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an enumerator that enumerates through the collection
        /// </summary>
        IEnumerator IEnumerable.GetEnumerator()
        {
            Queue<T> localQ;

            // init enumerator
            lockQ.EnterReadLock();
            try
            {
                // create a copy of m_TList
                localQ = new Queue<T>(_queue);
            }
            finally
            {
                lockQ.ExitReadLock();
            }

            // get the enumerator
            foreach (T item in localQ)
                yield return item;
        }

        #endregion

        #region ICollection Members

        #region CopyTo

        /// <summary>
        /// Copies the Queue's elements to an existing array
        /// </summary>
        /// <param name="array">the one-dimensional array to copy into</param>
        /// <param name="index">the zero-based index at which copying begins</param>
        public void CopyTo(Array array, int index)
        {
            lockQ.EnterReadLock();
            try
            {
                // copy
                _queue.ToArray().CopyTo(array, index);
            }

            finally
            {
                lockQ.ExitReadLock();
            }
        }

        /// <summary>
        /// Copies the Queue's elements to an existing array
        /// </summary>
        /// <param name="array">the one-dimensional array to copy into</param>
        /// <param name="index">the zero-based index at which copying begins</param>
        public void CopyTo(T[] array, int index)
        {
            lockQ.EnterReadLock();
            try
            {
                // copy
                _queue.CopyTo(array, index);
            }

            finally
            {
                lockQ.ExitReadLock();
            }
        }

        // CopyTo
        #endregion

        #region Count

        /// <summary>
        /// Returns the number of items in the Queue
        /// </summary>
        public int Count
        {
            get
            {
                lockQ.EnterReadLock();
                try
                {
                    return _queue.Count;
                }

                finally
                {
                    lockQ.ExitReadLock();
                }
            }
        }

        // Count
        #endregion

        #region IsSynchronized

        /// <summary>
        /// 
        /// </summary>
        public bool IsSynchronized
        {
            get { return true; }
        }

        // IsSynchronized
        #endregion

        #region SyncRoot

        /// <summary>
        /// 
        /// </summary>
        public object SyncRoot
        {
            get { return objSyncRoot; }
        }

        // SyncRoot
        #endregion

        #endregion

        #region Enqueue

        /// <summary>
        /// Adds an item to the queue
        /// </summary>
        /// <param name="item">the item to add to the queue</param>
        public void Enqueue(T item)
        {
            lockQ.EnterWriteLock();
            try
            {
                _queue.Enqueue(item);
            }

            finally
            {
                lockQ.ExitWriteLock();
            }
        }

        // Enqueue
        #endregion

        #region Dequeue

        /// <summary>
        /// Removes and returns the item in the beginning of the queue
        /// </summary>
        public T Dequeue()
        {
            lockQ.EnterWriteLock();
            try
            {
                return _queue.Dequeue();
            }

            finally
            {
                lockQ.ExitWriteLock();
            }
        }

        // Dequeue
        #endregion

        #region EnqueueAll

        /// <summary>
        /// Enqueues the list of items
        /// </summary>
        /// <param name="itemsToQueue">list of items to enqueue</param>
        public void EnqueueAll(IEnumerable<T> itemsToQueue)
        {
            lockQ.EnterWriteLock();
            try
            {
                // loop through and add each item
                foreach (T item in itemsToQueue)
                    _queue.Enqueue(item);
            }

            finally
            {
                lockQ.ExitWriteLock();
            }
        }

        /// <summary>
        /// Enqueues the list of items
        /// </summary>
        /// <param name="itemsToQueue">list of items to enqueue</param>
        public void EnqueueAll(List<T> itemsToQueue)
        {
            lockQ.EnterWriteLock();
            try
            {
                // loop through and add each item
                foreach (T item in itemsToQueue)
                    _queue.Enqueue(item);
            }

            finally
            {
                lockQ.ExitWriteLock();
            }
        }

        // EnqueueAll
        #endregion

        #region DequeueAll

        /// <summary>
        /// Dequeues all the items and returns them as a thread safe list
        /// </summary>
        public List<T> DequeueAll()
        {
            lockQ.EnterWriteLock();
            try
            {
                // create return object
                List<T> returnList = new List<T>();

                // dequeue until everything is out
                while (_queue.Count > 0)
                    returnList.Add(_queue.Dequeue());

                // return the list
                return returnList;
            }

            finally
            {
                lockQ.ExitWriteLock();
            }
        }

        // DequeueAll
        #endregion

        #region Clear

        /// <summary>
        /// Removes all items from the queue
        /// </summary>
        public void Clear()
        {
            lockQ.EnterWriteLock();
            try
            {
                _queue.Clear();
            }

            finally
            {
                lockQ.ExitWriteLock();
            }
        }

        // Clear
        #endregion

        #region Contains

        /// <summary>
        /// Determines whether the item exists in the Queue
        /// </summary>
        /// <param name="item">the item to find in the queue</param>
        public bool Contains(T item)
        {
            lockQ.EnterReadLock();
            try
            {
                return _queue.Contains(item);
            }

            finally
            {
                lockQ.ExitReadLock();
            }
        }

        // Contains
        #endregion

        #region Peek

        /// <summary>
        /// Returns the item at the start of the Queue without removing it
        /// </summary>
        public T Peek()
        {
            lockQ.EnterReadLock();
            try
            {
                return _queue.Peek();
            }

            finally
            {
                lockQ.ExitReadLock();
            }
        }

        // Peek
        #endregion

        #region ToArray

        /// <summary>
        /// Copies the Queue to a new array
        /// </summary>
        public T[] ToArray()
        {
            lockQ.EnterReadLock();
            try
            {
                return _queue.ToArray();
            }

            finally
            {
                lockQ.ExitReadLock();
            }
        }

        // ToArray
        #endregion

        #region TrimExcess

        /// <summary>
        /// Sets the capacity of the Queue to the current number of items, if that number
        /// is less than 90 percent of the current capacity
        /// </summary>
        public void TrimExcess()
        {
            lockQ.EnterWriteLock();
            try
            {
                _queue.TrimExcess();
            }

            finally
            {
                lockQ.ExitWriteLock();
            }
        }

        // TrimExcess
        #endregion
    }
}
